/*--------------------------------------------------------------------------------------------------
Yatak Operation System Project Copyright 2012-2013
Author : Ilya Churaev
Started : 19 Jan 2013		Updated : 4 Mar 2013
File Name : devices.c		Purpose : devices list, A standard interface to communicate with the device drivers.
						Functions to register a device, read/write from/to them,....
--------------------------------------------------------------------------------------------------*/
#include <yatak/devices.h>
#include <yatak/kheap.h>
#include <yatak/yatak.h>
#include <yatak/mutex.h>
#include <net/netdev.h>
#include <screen.h>
#include <sched.h>
#include <errno.h>
#include <msg.h>

list_devices_t * AddDevice (list_devices_t * head, PCI_Device_t * device, bool init_drive)
{
	list_devices_t * tmp;
	if (!head)
	{
		head = (list_devices_t *) kmalloc (sizeof(list_devices_t));
		tmp = head;
	}
	else
	{
		tmp = head;
		while (tmp->next) tmp = tmp->next;
		tmp->next = (list_devices_t *) kmalloc (sizeof(list_devices_t));
		tmp = tmp->next;
	}
	
	tmp->next = NULL;
	tmp->init_drive = init_drive;
	tmp->device.VendorID = device->VendorID;
	tmp->device.DeviceID = device->DeviceID;
	tmp->device.CommandReg = device->CommandReg;
	tmp->device.StatusReg = device->StatusReg;
	tmp->device.RevisionID = device->RevisionID;
	tmp->device.ProgIF = device->ProgIF;
	tmp->device.SubClass = device->SubClass;
	tmp->device.ClassCode = device->ClassCode;
	tmp->device.CachelineSize = device->CachelineSize;
	tmp->device.Latency = device->Latency;
	tmp->device.HeaderType = device->HeaderType;
	tmp->device.BIST = device->BIST;
	
	tmp->device.BAR0 = device->BAR0;
	tmp->device.BAR1 = device->BAR1;
	tmp->device.BAR2 = device->BAR2;
	tmp->device.BAR3 = device->BAR3;
	tmp->device.BAR4 = device->BAR4;
	tmp->device.BAR5 = device->BAR5;
	tmp->device.CardbusCISPtr = device->CardbusCISPtr;
	tmp->device.SubVendorID = device->SubVendorID;
	tmp->device.SubDeviceID = device->SubDeviceID;
	tmp->device.ExRomAddress = device->ExRomAddress;
	tmp->device.Reserved1 = device->Reserved1;
	tmp->device.Reserved2 = device->Reserved2;
	tmp->device.IRQ = device->IRQ;
	tmp->device.PIN = device->PIN;
	tmp->device.MinGrant = device->MinGrant;
	tmp->device.MaxLatency = device->MaxLatency;
	return head;
}


static uint32 count = 0;
static void PrintPciBiosInfo (PCI_BIOS_t * pci_bios)
{
	//uint32 flags;
	//save_flags (flags);
	//cli ();
	static mutex_t mutex;
	mutex_enter (&mutex);
	kprintf ("\tAddress: 0x%x\n",pci_bios);
	kprintf ("\tEntryPoint: 0x%x",pci_bios->EntryPoint);
	kprintf ("\tCRC: 0x%x\n",pci_bios->CRC);
	kprintf ("\tPages: 0x%x",pci_bios->Pages);
	kprintf ("\t\tVersion: 0x%x\n",pci_bios->Version);
	mutex_leave (&mutex);
	//restore_flags (flags);
}

static void PrintPciDevicesInfo (list_devices_t * list)
{
	//uint32 flags;
	//save_flags (flags);
	//cli ();
	static mutex_t mutex;
	mutex_enter (&mutex);
	list_devices_t * device = list;
	while (device)
	{
		switch(device->device.ClassCode)
		{
		//Pre-PCI Items
		case 0x00:
			if(device->device.SubClass == 0x01) //VGA
			{
				kprintf("\tVGA driver loading (legacy)... \n");
				break;
			}
			break;
		//Mass storage devices
		case 0x01:
			if(device->device.SubClass == 0x01) //IDE (And possibly CD?)
			{
				kprintf("\tATA (IDE) driver loading... [TODO]\n");
				break;
			}
			if(device->device.SubClass == 0x02) //Floppy
			{
				kprintf("\tFloppy driver loading (legacy)... [TODO]\n");
				break;
			}
			if(device->device.SubClass == 0x80)
			{
				kprintf("\tMass storage device controller driver loading... [TODO]\n");
				break;
			}
			break;
		//NIC's
		case 0x02:
			if(device->device.SubClass == 0x00)
			{
				//We need to find out which chipset here
				kprintf("\tEthernet driver loading... [TODO]\n");
				switch(device->device.VendorID)
				{
				/* Realtek RTL8139 and clones */
				case 0x10EC:
					if(device->device.DeviceID == 0x8139 || device->device.DeviceID == 0x8138)
					{
						kprintf("\tRealtek RTL8139 Discovered...\n");
					}
					break;
				case 0x1113:
					if(device->device.DeviceID == 0x1211)
					{
						kprintf("\tSMC1211TX EZCard 10/100 (RTL8139 clone) Discovered...\n");
					}
					break;
				case 0x1500:
					if(device->device.DeviceID == 0x1360)
					{
						kprintf("\tDelta Electronics 8139 10/100BaseTX (RTL8139 clone) Discovered...\n");
					}
					break;
				case 0x4033:
					if(device->device.DeviceID == 0x1360)
					{
						kprintf("\tAddtron Technology 8139 10/100BaseTX (RTL8139 clone) Discovered...\n");
					}
					break;
				case 0x1186:
					if(device->device.DeviceID == 0x1300)
					{
						kprintf("\tD-Link DFE-538TX (RTL8139 clone) Discovered...\n");
					}
					break;
				/* ------------------- */
				default:
					kprintf("\tUnknown ethernet card...\n");
					break;
				}
				break;
			}
			if(device->device.SubClass == 0x80)
			{
				kprintf("\tNetwork controller driver loading... [TODO]\n");
				break;
			}
			break;
		//Video
		case 0x03:
			if(device->device.SubClass == 0x00)
			{
				kprintf("\tVGA driver loading...\n");
				break;
			}
			if(device->device.SubClass == 0x01)
			{
				kprintf("\tXGA/VESA driver loading...[TODO]\n");
				break;
			}
			break;
			//Bridge Devices
			case 0x06:
			if(device->device.SubClass == 0x00)
			{
				kprintf("\tHost/PCI Bridge discovered... (driver active)\n");
				break;
			}
			if(device->device.SubClass == 0x01)
			{
				kprintf("\tPCI/ISA Bridge driver loading... [TODO]\n");
				break;
			}
			if(device->device.SubClass == 0x04)
			{
				kprintf("\tPCI/PCI Bridge driver loading... [TODO]\n");
				break;
			}
			break;
		//Other
		case 0x0C:
			if(device->device.SubClass == 0x03)
			{
				kprintf("\tUSB controller driver loading... [TODO]\n");
				break;
			}
			break;
		//None of the above
		default:
			break;
		}
		count++;
		device = device->next;
	}
	mutex_leave (&mutex);
	//restore_flags (flags);
}

static void PrintCpuInfo (struct cpu_info * cpu)
{
	//uint32 flags;
	//save_flags (flags);
	//cli ();
	static mutex_t mutex;
	mutex_enter (&mutex);
	switch(cpu->vendor_id)
	{
	case 0x756e6547: /* Intel Magic Code */
		kprintf ("\tIntel CPU Detected:\n");
		kprintf ("\tType %d - %s\n", cpu->type,cpu->type_str);
		kprintf ("\tFamily %d - %s\n", cpu->family, cpu->family_str);
		kprintf ("\tModel %d - %s\n", cpu->model, cpu->model_str);
		kprintf ("\tBrand: %s\n",cpu->brand);
		kprintf ("\tStepping: %d Reserved: %d\n", cpu->stepping, cpu->reserved);
		break;
	case 0x68747541: /* AMD Magic Code */
		kprintf ("\tAMD CPU Detected:\n");
		kprintf ("\tFamily: %d Model: %d [%s]\n", cpu->family, cpu->model,cpu->model_str);
		kprintf ("\tDetected Processor Name: %s\n",cpu->brand);
		kprintf ("\tStepping: %d Reserved: %d\n", cpu->stepping, cpu->reserved);
		break;
	case 0x69727943: /* Cyrix Magic Code */
		kprintf ("\tCyrix CPU Detected:\n");
		kprintf ("\tFamily: %d Model: %d [%s]\n", cpu->family, cpu->model,cpu->model_str);
		kprintf ("\tStepping: %d Reserved: %d\n", cpu->stepping, cpu->reserved);
		break;
	default:
		kprintf("\tUnknown x86 CPU Detected\n");
		break;
	}
	mutex_leave (&mutex);
	//restore_flags (flags);
}

void PrintSysInfo (system_info_t * yatak_info)
{
	//uint32 flags;
	//save_flags (flags);
	//cli ();
	static mutex_t mutex;
	mutex_enter (&mutex);
	int memusage = get_memory_usage();
	int total = 0;
	
	kprintf ("******************************* YATAK Information ******************************");
	kprintf ("Memory info:\n");
	//Memory
	kprintf ("\tMemory: %d Byte (%d MByte)\n",yatak_info->memory, yatak_info->memory/1024/1024);
	kprintf ("\tMemory usage: %d Byte (%d MByte)\n",memusage, memusage/1024/1024);
	// PCI Bus
	if (yatak_info->pci_bios)
	{
		// PCI BIOS
		kprintf ("PCI BIOS info:\n");
		PrintPciBiosInfo (yatak_info->pci_bios);
		// PCI DEVICES
		list_devices_t * device = yatak_info->devices;
		while (device)
		{
			total++;
			device = device->next;
		}
		kprintf ("PCI Devices info (Total = %d):\n",total);
		count = 10;
		PrintPciDevicesInfo (yatak_info->devices);
	}
	//CPU
	kprintf ("CPU info:\n");
	PrintCpuInfo (&(yatak_info->cpuinfo));
	kprintf ("********************************************************************************");
	//restore_flags (flags);
	mutex_leave (&mutex);
}



unsigned int cache_enabled = 0;
//extern int TASK_VFS;

struct s_devices devices[MAX_DEVICES] = {0};

struct s_devices net_devices[MAX_DEVICES] = {0};
unsigned int net_devices_counter = 0;
unsigned int devices_major = 1;

/**************************** Net devices ****************************/
int register_netdev(unsigned char*name, int pid)
{
	net_devices[net_devices_counter].major = net_devices_counter;
	net_devices[net_devices_counter].pid = pid;
	strcpy(net_devices[net_devices_counter].name,name);
	net_devices_counter++;
	
	return (net_devices_counter-1);
}

void write_netdev(unsigned int major, struct s_net_buff *buff)
{
	struct ipc_message m;
	m.type = MSG_WRITE_BLOCK;
	
	m.MSGT_WRITE_BLOCK_BUFFER = buff;
	
	send_msg(net_devices[major].pid, &m);
	return SUCCESS;
}

/**************************** Block devices **************************/
void register_blkdev(int major, unsigned char* name, int pid)
{
	devices[major].major = devices_major;
	devices[major].pid = pid;
	strcpy(devices[major].name, name);
	devices_major++;
}

int read_blkdev(struct ipc_message *m)
{
	int major;
	int blocknr;
	unsigned char buffer[512];
	
	major = name2device(m->MSGT_READ_BLOCK_DEVICENAME);
	blocknr = m->MSGT_READ_BLOCK_BLOCKNUMMER;
	
	if (cache_enabled)
	{
		// Is Block in Cache?
		if (1/*cache_hit(major, blocknr, buffer)*/)
		{
			memcpy(m->MSGT_READ_BLOCK_BUFFER, buffer, 512);
			m->type = MSG_READ_BLOCK_CACHE_REPLY;
			//send_msg(TASK_VFS, m);
			return SUCCESS;
		}
	}
	
	m->MSGT_READ_BLOCK_MINOR = major;
	send_msg(devices[major].pid, m);
	
	return SUCCESS;
}

int write_blkdev(struct ipc_message *m)
{
	int major;
	int blocknr;
	unsigned char buffer[512];
	
	major = name2device(m->MSGT_WRITE_BLOCK_DEVICENAME);
	blocknr = m->MSGT_WRITE_BLOCK_BLOCKNUMMER;
	
	m->MSGT_WRITE_BLOCK_MINOR = major;
	
	send_msg(devices[major].pid, m);
	return SUCCESS;
}

/**************************** Character Devices **************************/
void register_device(int major, unsigned char* name, int pid)
{
	devices[major].major = devices_major;
	devices[major].pid = pid;
	strcpy(devices[major].name, name);
	devices_major++;
}

int read_chardev(struct ipc_message *m)
{
	int ret;
	int major;
	unsigned char* buffer;
	major = name2device(m->MSGT_READ_CHAR_DEVICENAME);

//  m->source->waitingfor = devices[major].pid;
	kfree(m->MSGT_READ_BLOCK_DEVICENAME);
	
	send_msg(devices[major].pid, m);
	
	return ret;
}

int name2device(unsigned char *name)
{
	int i;
	for (i=0;i<MAX_DEVICES;i++)
		if (!strcmp(name,devices[i].name))
			return i;
	for (i=0;i<MAX_DEVICES;i++)
		if (!strcmp(name,net_devices[i].name))
			return i;
	return -1;
}

int device_getmajor(unsigned int i)
{
	return devices[i].major;
}


int name2task(unsigned char *name)
{
	int i;
	
	for (i=0;i<MAX_DEVICES;i++)
		if (!strcmp(name,devices[i].name))
			return devices[i].pid;
	for (i=0;i<MAX_DEVICES;i++)
		if (!strcmp(name,net_devices[i].name))
			return net_devices[i].pid;
	return -1;
}

void devices_dump(void)
{
	int i;
	static mutex_t mutex;
	mutex_enter (&mutex);
	
	kprintf("Dumping device status: \n\n");
	kprintf("--------------------------------------------------------------------------");
	kprintf("Major  |  Name");
	kprintf("--------------------------------------------------------------------------");
	for (i = 0; i< MAX_DEVICES; i++)
	{
		kprintf("%00005d  |  %-20s",
				devices[i].major, devices[i].name
/*				devices[i].fops->open,
				devices[i].fops->read,
				devices[i].fops->write*/
			   );
	}
	mutex_leave(&mutex);
}